﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Interactivity;

namespace ERDDiagram.Helpers
{
    public class KeyboardSelectionBehavior : Behavior<Selector>
    {
        /// <summary>
        /// Gets or sets the Path used to select the text
        /// </summary>
        public string SelectionMemberPath { get; set; }

        public KeyboardSelectionBehavior() { }

        /// <summary>
        /// Attaches to the specified object: subscribe on KeyDown event
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.KeyDown += DoKeyDown;
        }

        void DoKeyDown(object sender, KeyEventArgs e)
        {

            // Create a list of strings and indexes
            int index = 0;
            IEnumerable<Item> list = null;
            var path = SelectionMemberPath ??
                this.AssociatedObject.DisplayMemberPath;
            var evaluator = new BindingEvaluator();
            if (path != null)
            {
                list = this.AssociatedObject.Items.OfType<object>()
                    .Select(item =>
                    {
                        // retrieve the value using the supplied Path
                        var binding = new Binding();
                        binding.Path = new PropertyPath(path);
                        binding.Source = item;

                        BindingOperations.SetBinding(evaluator,
                            BindingEvaluator.TargetProperty, binding);
                        var value = evaluator.Target;

                        return new Item
                        {
                            Index = index++,
                            Text = Convert.ToString(value)
                        };
                    });
            }
            else
            {
                list = this.AssociatedObject.Items.OfType<ListBoxItem>()
                    .Select(item => new Item
                    {
                        Index = index++,
                        Text = Convert.ToString(item.Content)
                    });
            }
            // Sort the list starting at next selectedIndex to the end and
            // then from the beginning
            list = list.OrderBy(item => item.Index <=
                this.AssociatedObject.SelectedIndex ?
                item.Index + this.AssociatedObject.Items.Count : item.Index);

            // Find first starting with
            var text = e.Key.ToString();
            var first = list.FirstOrDefault(item => item.Text.StartsWith(text,
                StringComparison.InvariantCultureIgnoreCase));
            if (first != null)
            {
                // found
                this.AssociatedObject.SelectedIndex = first.Index;
            }
        }

        /// <summary>
        /// Helper class
        /// </summary>
        private class Item
        {
            public int Index;
            public string Text;
        }

        /// <summary>
        /// Helper class used for property path value retrieval
        /// </summary>
        private class BindingEvaluator : FrameworkElement
        {

            public static readonly DependencyProperty TargetProperty =
                DependencyProperty.Register(
                    "Target",
                    typeof(object),
                    typeof(BindingEvaluator), null);

            public object Target
            {
                get { return GetValue(TargetProperty); }
                set { SetValue(TargetProperty, value); }
            }
        }

    }
}